﻿namespace ChangchunPulbicServicesImp;

public class TravelSubjectServices : ITravelSubject
{
    private IConfigServices ConfigServices { get; init; }

    private IHttpClientFactory HttpClientFactory { get; init; }

    public TravelSubjectServices(IConfigServices configServices, IHttpClientFactory httpClientFactory)
    {
        ConfigServices = configServices;
        HttpClientFactory = httpClientFactory;
    }

    public async Task<TrafficResult> RealTimeTrafficQueryAsync(string roadName)
    {
        ///验证参数
        if (!ValidationHelper.IsRoadNameLegal(roadName))
            return new TrafficResult()
            {
                StatusCode = Code.IllegalArgs,
                Message = "路名格式不符合要求",
            };

        ///请求API
        var client = HttpClientFactory.CreateClient();
        var apiResult = await APIHelper.GetAsync<Traffic>(client,
            $"https://api.map.baidu.com/traffic/v1/road?road_name={roadName}&city={ConfigServices.CityName}&ak={ConfigServices.AK}");

        ///查询失败
        if (!apiResult.Succeed)
            return new TrafficResult()
            {
                StatusCode = apiResult.StatusCode,
                Message = "查询失败",
                QueryResultFrom3rdPartyApi = apiResult,
            };

        ///查询成功
        return new TrafficResult()
        {
            StatusCode = Code.Succeed,
            Message = "请求成功",
            Traffic = apiResult.ResultObject,
        };
    }

    public async Task<RescueInfoResult> RescueInfoQueryAsync(Point accidentLocation)
    {
        ///请求API
        var client = HttpClientFactory.CreateClient();
        var apiResult = await APIHelper.GetAsync<Place>(client,
            $"https://api.map.baidu.com/place/v2/search?query=医院&tag=急救中心&location={accidentLocation.ToReverseCoordinate()}&radius=10000&output=json&ak={ConfigServices.AK}");

        ///查询失败
        if (!apiResult.Succeed)
            return new RescueInfoResult()
            {
                StatusCode = apiResult.StatusCode,
                Message = "查询失败",
                QueryResultFrom3rdPartyApi = apiResult,
            };
        ///验证结果
        var resultObj = apiResult.ResultObject;

        ///解析错误
        if (resultObj is null)
            return new RescueInfoResult()
            {
                StatusCode = Code.InternalFault,
                Message = "解析查询结果时，程序发生内部错误",
                QueryResultFrom3rdPartyApi = apiResult,
            };
        if (resultObj.results is null)
            return new RescueInfoResult()
            {
                StatusCode = Code.InternalFault,
                Message = "解析查询结果时，程序发生内部错误",
                QueryResultFrom3rdPartyApi = apiResult,
            };
        if (resultObj.results.Count is 0)
            return new RescueInfoResult()
            {
                StatusCode = Code.NoQueryResult,
                Message = "查询成功，但附近没有可用的医疗点",
                QueryResultFrom3rdPartyApi = apiResult,
            };

        ///规划路径
        var hospitals = resultObj.results;
        var tasks = new List<Task<RoutePlanningResult>>(hospitals.Count);
        for (int i = 0; i < hospitals.Count; i++)
        {
            var hp = hospitals[i].location;
            if (hp is null)
                return new RescueInfoResult()
                {
                    StatusCode = Code.InternalFault,
                    Message = "解析地址时发生内部错误",
                    QueryResultFrom3rdPartyApi = apiResult,
                };
            var task = SingleRoutPlanningAsync(accidentLocation, new Point(hp.lng, hp.lat), TravelMode.Driving, 3);
            tasks.Add(task);
        }

        RoutePlanningResult[] routesToHospitals;
        try
        {
            routesToHospitals = await Task.WhenAll(tasks);
        }
        catch (Exception e)
        {
            return new RescueInfoResult()
            {
                StatusCode = Code.InternalFault,
                Message = "查询路径时发生内部错误",
                SourceException = e,
                QueryResultFrom3rdPartyApi = apiResult,
            };
        }

        ///选择最近的医院与路线
        var routesList = new List<RoutePlanningResult>(routesToHospitals);
        var orderedList = (new List<RoutePlanningResult>(routesToHospitals)).OrderBy(item => item.Distance).ToList();
        var recommendedHospital = hospitals[routesList.IndexOf(orderedList[0])];
        var recommendedRoute = orderedList[0].Steps;

        return new RescueInfoResult()
        {
            StatusCode = Code.Succeed,
            Message = "查询成功",
            Hospitals = resultObj.results,
            RecommendedHospital = recommendedHospital,
            RecommendedRoute = recommendedRoute,
            Distance = orderedList[0].Distance,
            Duration = orderedList[0].Duration,
        };
    }

    public async Task<PoiQueryResult> ScenicSpotKeywordQueryAsync(string keyword, int page)
    {
        ///验证参数
        if (!ValidationHelper.IsKeywordLegal(keyword))
            return new PoiQueryResult()
            {
                StatusCode = Code.IllegalArgs,
                Message = "关键字格式不符合要求",
            };

        ///请求API
        var client = HttpClientFactory.CreateClient();
        var apiResult = await APIHelper.GetAsync<Place>(client,
            $"https://api.map.baidu.com/place/v2/search?query={keyword} 景点&region={ConfigServices.CityName}&city_limit=true&page_num={page}&output=json&ak={ConfigServices.AK}");

        ///查询失败
        if (!apiResult.Succeed)
            return new PoiQueryResult()
            {
                StatusCode = apiResult.StatusCode,
                Message = "查询失败",
                QueryResultFrom3rdPartyApi = apiResult,
            };

        ///查询成功
        return new PoiQueryResult()
        {
            StatusCode = Code.Succeed,
            Message = "请求成功",
            QueryResultFrom3rdPartyApi = apiResult,
        };
    }

    public async Task<RoutePlanningResult> TravelRoutePlanningAsync(TravelMode travelMode, params Point[] waypoints)
    {
        ///验证数据
        var pointsNumber = waypoints.Length;
        if (pointsNumber > 10)
            return new RoutePlanningResult()
            {
                StatusCode = Code.IllegalArgs,
                Message = "中途点数量不能超过10",
            };

        ///转换为单路径规划
        if (pointsNumber is 2)
            return await SingleRoutPlanningAsync(waypoints[0], waypoints[1], travelMode);

        ///获取任意两地点之间的距离与路线规划
        var costNumber = pointsNumber * (pointsNumber - 1) / 2;
        var tasks = new List<Task<RoutePlanningResult>>(costNumber);
        for (int i = 0; i < pointsNumber; i++)
        {
            for (int j = 1 + i; j < pointsNumber; j++)
            {
                var task = SingleRoutPlanningAsync(waypoints[i], waypoints[j], travelMode);
                tasks.Add(task);
            }
        }
        RoutePlanningResult[] every2pointsResults;
        try
        {
            every2pointsResults = await Task.WhenAll(tasks);
        }

        ///获取时发生异常
        catch (Exception e)
        {
            return new RoutePlanningResult()
            {
                StatusCode = Code.InternalFault,
                Message = $"获取城市间距离时，程序发生内部错误：{e.Message}",
                SourceException = e,
            };
        }

        ///获取完成，验证获取结果
        foreach (var item in every2pointsResults)
        {
            if (item is null)
                return new RoutePlanningResult()
                {
                    StatusCode = Code.InternalFault,
                    Message = $"获取城市间距离时，程序发生内部错误",
                };

            if (!item.Succeed)
                return new RoutePlanningResult()
                {
                    StatusCode = item.StatusCode,
                    Message = item.Message,
                };
        }

        ///全部获取成功，保存查询结果
        var distances = new double[costNumber];
        //var durations=new int[distanceNumber];
        //var tolls=new int[distanceNumber];
        for (int i = 0; i < costNumber; i++)
        {
            distances[i] = every2pointsResults[i].Distance;
            //durations[i] = every2pointsResults[i].Duration;
            //tolls[i] = every2pointsResults[i].Toll;
        }

        ///开始规划路线
        var calculator = new TSPCalculator((byte)pointsNumber, distances);
        (byte[] ResultRoute, double TotalCost) calResult;
        try
        {
            calResult = await calculator.CalculateAsync(1000);
        }

        ///规划出现异常
        catch (Exception e)
        {
            return new RoutePlanningResult()
            {
                StatusCode = Code.InternalFault,
                Message = "在规划路线时，程序发生内部错误",
                SourceException = e,
            };
        }

        ///规划完成，开始获取具体路径
        var singleTasks = new List<Task<RoutePlanningResult>>(pointsNumber - 1);
        var routeByID = ConvertHelper.ByteArray2IntArray(calResult.ResultRoute);

        ///规划相邻点的路径
        for (int i = 0; i < pointsNumber - 1; i++)
        {
            var task = SingleRoutPlanningAsync(waypoints[routeByID[i]], waypoints[routeByID[i + 1]], travelMode);
            singleTasks.Add(task);
        }
        RoutePlanningResult[] adjacentpointsResults;
        try
        {
            adjacentpointsResults = await Task.WhenAll(singleTasks);
        }

        ///获取时发生异常
        catch (Exception e)
        {
            return new RoutePlanningResult()
            {
                StatusCode = Code.InternalFault,
                Message = $"获取两点间路径时，程序发生内部错误：{e.Message}",
                SourceException = e,
            };
        }

        ///组装数据
        var route = new List<Step>();
        var dis = 0;
        var du = 0;
        var toll = 0;
        for (int i = 0; i < pointsNumber - 1; i++)
        {
            var temp = adjacentpointsResults[i];
            ///验证数据
            if (!temp.Succeed)
                return new RoutePlanningResult()
                {
                    StatusCode = adjacentpointsResults[i].StatusCode,
                    Message = adjacentpointsResults[i].Message,
                    QueryResultFrom3rdPartyApi = adjacentpointsResults[i].QueryResultFrom3rdPartyApi,
                };
            if (temp.Steps is null)
                return new RoutePlanningResult()
                {
                    StatusCode = Code.InternalFault,
                    Message = $"解析两点间路径时，程序发生内部错误",
                };

            route.AddRange(temp.Steps);
            dis += adjacentpointsResults[i].Distance;
            du += adjacentpointsResults[i].Duration;
            toll += adjacentpointsResults[i].Toll;
        }

        return new RoutePlanningResult()
        {
            StatusCode = Code.Succeed,
            Message = "路线规划成功",
            BestRouteByID = ConvertHelper.ByteArray2IntArray(calResult.ResultRoute),
            Distance = dis,
            Steps = route,
            Duration = du,
            Toll = toll,
        };
    }

    /// <summary>
    /// 两点间路径规划
    /// </summary>
    /// <param name="origin">起点</param>
    /// <param name="destination">终点</param>
    /// <param name="travelMode">出行方式</param>
    /// <param name="tactics">路线偏好。仅在出行方式为驾车时有效（可选值：0：常规路线，1：不走高速，2：躲避拥堵，3：距离较短）</param>
    /// <returns>查询结果</returns>
    private async Task<RoutePlanningResult> SingleRoutPlanningAsync(Point origin, Point destination, TravelMode travelMode, int tactics = 0)
    {
        var client = HttpClientFactory.CreateClient();
        var apiResult = await APIHelper.GetAsync<Directionlite>(client,
            $"https://api.map.baidu.com/directionlite/v1/{travelMode.ToString().ToLower()}?origin={origin.ToReverseCoordinate()}&" +
            $"destination={destination.ToReverseCoordinate()}&ak={ConfigServices.AK}"+ (travelMode == TravelMode.Driving ? $"&tactics={tactics}" : ""));

        ///查询失败
        if (!apiResult.Succeed)
            return new RoutePlanningResult()
            {
                StatusCode = apiResult.StatusCode,
                Message = "查询失败",
                QueryResultFrom3rdPartyApi = apiResult
            };

        ///查询成功，解析失败
        var nullResult = new RoutePlanningResult()
        {
            StatusCode = Code.InternalFault,
            Message = "查询成功，但是解析失败",
            QueryResultFrom3rdPartyApi = apiResult,
        };
        if (apiResult.ResultObject is null) return nullResult;
        if (apiResult.ResultObject.result is null) return nullResult;
        if (apiResult.ResultObject.result.routes is null) return nullResult;

        ///解析成功
        var resultRoute = apiResult.ResultObject.result.routes[0];
        return new RoutePlanningResult()
        {
            StatusCode = Code.Succeed,
            Message = "查询成功",
            Distance = resultRoute.distance,
            Duration = resultRoute.duration,
            Toll = resultRoute.toll,
            Steps = resultRoute.steps
        };
    }
}
